package org.goldenport.g3.adapters

import java.io._
import java.net.URL
import org.goldenport.wpath._
import org.goldenport.g3._
import org.goldenport.g3.atom._
import org.goldenport.g3.messages._
import org.goldenport.g3.messages.filestore._
import org.goldenport.g3.events._
import org.goldenport.g3.events.filestore._
import com.asamioffice.goldenport.io.{ UFile, UURL, UIO }

/**
 * @since   Jul. 20, 2010
 * @version Oct. 10, 2010
 * @author  ASAMI, Tomoharu
 */
abstract class G3FileStoreAdapter() extends G3Adapter() {
  protected override def do_Action(content: AnyRef, parcel: G3Parcel, ctx: G3AgentContext): AnyRef = {
    content match {
      case create: Create => create_file(create.path, create.openInputStream())
      case read: Read => read_files(read.path)
      case write: Write => write_file(write.path, write.openInputStream())
      case remove: Remove => remove_file(remove.path)
      case get: Get => read_files(get.uri)
      case post: Post => create_files(post.uri, post.content.get)
      case put: Put => write_files(put.uri, put.content.get)
      case delete: Delete => remove_file(delete.uri)
      case _ => error("???:" + content)
    }
  }

  private def create_files(path: String, content: AnyRef): AnyRef = {
    val driver = create_driver()
    driver.open()
    try {
      driver.createFiles(path, content)
    } finally {
      driver.close()
    }
  }

  private def create_file(path: String, in: Option[InputStream]): AnyRef = {
    val driver = create_driver()
    driver.open()
    try {
      driver.createFiles(path, in)
    } finally {
      driver.close()
    }
  }

  private def read_files(path: String): AnyRef = {
    val driver = create_driver()
    driver.open()
    try {
      driver.readFiles(path)
    } finally {
      driver.close()
    }
  }

  private def write_files(path: String, content: AnyRef): AnyRef = {
    val driver = create_driver()
    driver.open()
    try {
      driver.writeFiles(path, content)
    } finally {
      driver.close()
    }
  }

  private def write_file(path: String, in: InputStream): AnyRef = {
    val driver = create_driver()
    driver.open()
    try {
      driver.writeFiles(path, in)
    } finally {
      driver.close()
    }
  }

  private def remove_file(path: String): AnyRef = {
    val driver = create_driver()
    driver.open()
    try {
      driver.removeFile(path)
    } finally {
      driver.close()
    }
  }

  private final def create_driver(): G3FileStoreDriver = {
    val driver = create_Driver()
    driver.setContext(context)
    driver
  }

  protected def create_Driver(): G3FileStoreDriver
}

abstract class G3FileStoreDriver extends WPathProvider with G3Contextable {
  type NODE = G3FileStoreNode
  val root_Node = new G3FileStoreNode("")

  def open() {
  }

  def close() {
  }

  def createFiles(path: String, aContent: AnyRef): FileCreated = {
    val wpath = WPath(path)
    val result = aContent match {
      case record: Record => create_Record(wpath, record)
      case records: RecordSet => create_Records(wpath, records)
      case feed: AtomFeed => create_Feed(wpath, feed)
      case entry: AtomEntry => create_Entry(wpath, entry)
      case Some(in: InputStream) => create_file(wpath, in)
      case in: InputStream => create_file(wpath, in)
      case s: String => create_file(wpath, s)
      case None => create_file(wpath)
    }
    new FileCreated(path) {
      setInput(aContent)
      setOutput(result)
    }
  }

  private def make_message(content: AnyRef): G3Message = {
    content match {
      case msg: G3Message => msg
      case _ => Envelope(content)
    }
  }

  private def make_result(result: AnyRef): Option[AnyRef] = {
    result match {
      case None => None
      case Some(x: AnyRef) => Some(x)
      case _ => Some(result)
    }
  }

  private def create_file(wpath: WPath): AnyRef = {
    create_File(wpath.container, wpath.leafName)
  }

  private def create_file(wpath: WPath, in: InputStream): AnyRef = {
    create_File(wpath.container, wpath.leafName, in)
  }

  private def create_file(wpath: WPath, string: String): AnyRef = {
    create_File(wpath.container, wpath.leafName, string)
  }

  protected def create_Record(wpath: WPath, record: Record): AnyRef = {
    val in = record.openInputStream()
    try {
      create_File(wpath, record.idString, in)
    } finally {
      in.close()
    }
  }

  protected def create_Records(wpath: WPath, records: RecordSet): AnyRef = {
    for (record <- records) {
      create_Record(wpath, record)
    }
    None
  }

  protected def create_Feed(wpath: WPath, feed: AtomFeed): AnyRef = {
    for (entry <- feed.atomEntries) {
      create_Entry(wpath, entry)
    }
    None
  }

  protected def create_Entry(wpath: WPath, entry: AtomEntry): AnyRef = {
    create_File(wpath, entry.atomId.value, entry.contentText)
  }

  protected def create_File(wpath: WPath, id: String): AnyRef

  protected def create_File(wpath: WPath, id: String, string: String): AnyRef

  protected def create_File(wpath: WPath, id: String, in: InputStream): AnyRef

  def readFiles(path: String) = {
    val wpath = WPath(path)
    val x = read_Files(wpath)
//    println("G3FileRead = " + x)
    x
  }

  protected def read_Files(wpath: WPath): AnyRef

  def writeFiles(path: String, content: AnyRef): FileWrited = {
    val wpath = WPath(path)
    val result = content match {
      case record: Record => write_Record(wpath, record)
      case records: RecordSet => write_Records(wpath, records)
      case feed: AtomFeed => write_Feed(wpath, feed)
      case entry: AtomEntry => write_Entry(wpath, entry)
      case Some(in: InputStream) => write_file(wpath, in)
      case in: InputStream => write_file(wpath, in)
      case s: String => write_file(wpath, s)
      case None => write_file(wpath)
    }
    new FileWrited(path) {
      setInput(content)
      setOutput(result)
    }
  }

  private def write_file(wpath: WPath): AnyRef = {
    write_File(wpath.container, wpath.leafName)
  }

  private def write_file(wpath: WPath, in: InputStream): AnyRef = {
    write_File(wpath.container, wpath.leafName, in)
  }

  private def write_file(wpath: WPath, string: String): AnyRef = {
    write_File(wpath.container, wpath.leafName, string)
  }

  protected def write_Record(wpath: WPath, record: Record): AnyRef = {
    val in = record.openInputStream()
    try {
      write_File(wpath, record.idString, in)
    } finally {
      in.close()
    }
  }

  protected def write_Records(wpath: WPath, records: RecordSet): AnyRef = {
    for (record <- records) {
      write_Record(wpath, record)
    }
    None
  }

  protected def write_Feed(wpath: WPath, feed: AtomFeed): AnyRef = {
    for (entry <- feed.atomEntries) {
      write_Entry(wpath, entry)
    }
    None
  }

  protected def write_Entry(wpath: WPath, entry: AtomEntry): AnyRef = {
    write_File(wpath, entry.atomId.value, entry.contentText)
  }

  protected def write_File(wpath: WPath, id: String): AnyRef

  protected def write_File(wpath: WPath, id: String, string: String): AnyRef

  protected def write_File(wpath: WPath, id: String, in: InputStream): AnyRef

  def removeFile(path: String) = {
    val wpath = WPath(path)
    remove_File(wpath)
  }

  protected def remove_File(wpath: WPath): AnyRef

  protected final def get_basic_auth(key: String) = {
    val auth = context.getAuthentication(key)
    auth match {
      case Some(bauth: BasicAuthentication) => {
        val user = bauth.user
        val password = bauth.password
        (user, password)
      }
      case Some(oauth: OAuthAuthentication) => {
        error(auth.toString) // XXX
      }
      case None => error(auth.toString)
    }    
  }
}

class G3FileStoreNode(val name: String) extends WPathNode {
  type CONTENT = AnyRef
  type NODE = G3FileStoreNode

  override def create_Node(name: String) = new G3FileStoreNode(name)
}
